// 均匀二维网格

#include "dshSmoothU2d.h"

#include "../global.h"
#include <cstdio>
#include <iostream>
#include <cstring>
#include "initVars.h"
#include "diff2d.h"
#include "fdmU2d.h"


using namespace std;
using namespace u2d;
using namespace u2dMesh;
using namespace u2dICBC;
using namespace u2dFun;

int main()
{
    u2d::init();
    cout<<"Done init."<<endl<<"Solve:"<<endl;
    u2dOut::writeData((char *)"./data/phi0.csv",u2d::x0);
    int bk=0;
    int iterNum=0;
    time_t start,current,end;
    start=clock();
    while(bk==0||iterNum==0){
        current=clock();
        cout<<"time:"<<double(current-start)/1e06<<"s\t";
        cout<<"iter:"<<iterNum<<"\t";
        bk=fdmU2d::iter(u2d::diffFun,u2dMesh::xNum,u2dMesh::yNum,u2d::x0,u2d::b,u2d::x1);
        u2d::updateX0();
        iterNum++;
    }
    u2dOut::writeData((char *)"./data/phi.csv",u2d::x1);
    end=clock();
    cout<<"Done solved. "<<endl;
    cout<<"runtime:"<<double(end-start)/1e06<<"s"<<endl;
    return 1;
}

void u2d::init(){
    readFun();
    initMesh();
    readICBC();
    initLap();
    genDiffFunction();
    genBPts();
    genEqu();
    u2dOut::writeData((char *)"./data/x.csv",xMat);
    u2dOut::writeData((char *)"./data/y.csv",yMat);
    fdmU2d::copyBc(xNum,yNum,x0,x1);
    fdmU2d::readScheme((char *)"./scheme/iterScheme");
    // 
    return;
}

void u2d::initLap()
{
    diff2D::diffList tempListX,tempListY;
    lap.ptAppend(0,0,0);
    tempListX.ptAppend(0,0,-2);
    tempListX.ptAppend(1,0,1);
    tempListX.ptAppend(-1,0,1);
    tempListX.divided(dx*dx);
    tempListY.ptAppend(0,0,-2);
    tempListY.ptAppend(0,1,1);
    tempListY.ptAppend(0,-1,1);
    tempListY.divided(dy*dy);
    lap.add(tempListX);
    lap.add(tempListY);
    // lap.print();
    return;
}

void u2d::genDiffFunction()
{   
    // Used to generate the difference scheme.
    diffFun.ptAppend(0,0,1);
    diff2D::diffList tempList;
    tempList.add(lap);
    tempList.times(-eps);
    diffFun.add(tempList);
    diffFun.print();
    return;
}

void u2d::initMesh()
{
    readMesh((char *)"./mesh/u2dMesh");
    dx=(xMax-xMin)/(xNum-1);
    dy=(yMax-yMin)/(yNum-1);
    cout<<"1"<<endl;
    xMat=zeros2D(xNum,yNum);
    yMat=zeros2D(xNum,yNum);
    cout<<"2"<<endl;
    for(int p=0;p<xNum;p++){
        for(int q=0;q<yNum;q++){
            xMat[p][q]=xMin+p*dx;
            yMat[p][q]=yMin+q*dy;
        }
    }
    cout<<"3"<<endl;
    return;
}

void u2d::calc()
{
    init();
    cout<<"Done init."<<endl<<"Solve:"<<endl;
    u2dOut::writeData((char *)"./data/phi0.csv",x0);
    int bk=0;
    int iterNum=0;
    time_t start,current,end;
    start=clock();
    while(bk==0||iterNum==0){
        current=clock();
        cout<<"time:"<<double(current-start)/1e06<<"s\t";
        cout<<"iter:"<<iterNum<<"\t";
        bk=fdmU2d::iter(diffFun,xNum,yNum,x0,b,x1);
        updateX0();
        iterNum++;
    }
    u2dOut::writeData((char *)"./data/phi.csv",x1);
    end=clock();
    cout<<"Done solved. "<<endl;
    cout<<"runtime:"<<double(end-start)/1e06<<"s"<<endl;
    return;
}

int u2d::genEqu()
{
    // init var
    x0=zeros2D(xNum,yNum);
    x1=zeros2D(xNum,yNum);
    b=zeros2D(xNum,yNum);
    // copy phi to b
    for(int k=0;k<xNum;k++){
        for(int p=0;p<yNum;p++){
            b[k][p]=phi[k][p]/diffFun.dCoef[0];
        }
    }
    diffFun.divided(diffFun.dCoef[0]);
    // copy phi to x0(IC)
    for(int k=0;k<xNum;k++){
        for(int p=0;p<yNum;p++){
            x0[k][p]=phi[k][p];
        }
    }
    return 1;
}

int u2d::updateX0()
{
    for(int k=0;k<xNum;k++){
        for(int p=0;p<yNum;p++){
            x0[k][p]=x1[k][p];
        }
    }
    return 1;
}

void u2dMesh::readMesh(char *fileName)
{
    char filePath[256];
    strcpy(filePath,currentPath);
    strcat(filePath,fileName);
    cout<<"Reading u2dMesh from "<<filePath<<endl;
    //freopen(filePath,"r",stdin);
    ifstream fin(filePath);
    streambuf *oldcin;
    oldcin = cin.rdbuf(fin.rdbuf());
    string tempstr;
    while(cin>>tempstr){
        if(tempstr=="xMin")
            cin>>xMin;
        if(tempstr=="xMax")
            cin>>xMax;
        if(tempstr=="yMin")
            cin>>yMin;
        if(tempstr=="yMax")
            cin>>yMax;
        if(tempstr=="xNum")
            cin>>xNum;
        if(tempstr=="yNum")
            cin>>yNum;
        cout<<"\tReading "<<tempstr<<" successe. "<<endl;
    }
    //fclose(stdin);
    cin.rdbuf(oldcin);
    fin.close();
    cout<<"\txNum="<<xNum<<endl;
    cout<<"\tyNum="<<yNum<<endl;
    cout<<"\txMin="<<xMin<<endl;
    cout<<"\txMax="<<xMax<<endl;
    cout<<"\tyMin="<<yMin<<endl;
    cout<<"\tyMax="<<yMax<<endl;
    cout<<"Done reading u2dMesh."<<endl;
    return;
}

int u2dICBC::readICBC()
{
    return readIC((char *)"./icbc/ic");
}

int u2dICBC::readIC(char *fileName)
{
    char filePath[256];
    strcpy(filePath,currentPath);
    strcat(filePath,fileName);
    cout<<"Reading u2dIC from "<<filePath<<endl;
    ifstream fin(filePath);
    streambuf *oldcin;
    oldcin = cin.rdbuf(fin.rdbuf());

    string icType;
    string varType;
    int nCurrent;
    cin>>icType;
    if(icType=="fourier2D"){
        cin>>nMax;
        for(int k=0;k<nMax;k++){
            cin>>nCurrent;
            if(nCurrent>=nMax){
                cout<<"FATAL ERROR: nCurrent>=nMax"<<endl;
                return 0;
            }
            for(int m=0;m<6;m++){
                cin>>varType;
                if(varType=="omegaX"){
                    cin>>u2dICBC::omegaX[k];
                }
                else if(varType=="omegaY"){
                    cin>>u2dICBC::omegaY[k];
                }
                else if(varType=="phiX"){
                    cin>>u2dICBC::phiX[k];
                }
                else if(varType=="phiY"){
                    cin>>u2dICBC::phiY[k];
                }
                else if(varType=="aX"){
                    cin>>u2dICBC::aX[k];
                }
                else if(varType=="aY"){
                    cin>>u2dICBC::aY[k];
                }
            }
        }
    }
    cout<<"\tInitial Conditions:"<<endl;
    cout<<"\tphi0 = ";
    for(int k=0;k<nMax;k++){
        if(aX[k]!=1){
            cout<<aX[k];
        }
        cout<<"cos(";
        if(omegaX[k]!=1){
            cout<<omegaX[k];
        }
        cout<<"x";
        if(phiX[k]!=0){
            cout<<"+"<<phiX[k];
        }
        cout<<")+";
        if(aY[k]!=1){
            cout<<aY[k];
        }
        cout<<"cos(";
        if(omegaY[k]!=1){
            cout<<omegaY[k];
        }
        cout<<"x";
        if(phiY[k]!=0){
            cout<<"+"<<phiY[k];
        }
        cout<<")"<<endl;
    }

    cin.rdbuf(oldcin);
    fin.close();
    cout<<"Done reading u2dIC."<<endl;
    return 1;
}

int u2dFun::readEps(char *fileName)
{
    char filePath[256];
    strcpy(filePath,currentPath);
    strcat(filePath,fileName);
    cout<<"Reading eps from "<<filePath<<endl;
    //freopen(filePath,"r",stdin);
    ifstream fin(filePath);
    streambuf *oldcin;
    oldcin = cin.rdbuf(fin.rdbuf());
    string tempstr;
    int tempInt;
    double tempDouble;
    int i=0;
    while(cin>>tempstr){
        cin>>eps;
        cout<<'\t'<<tempstr<<" = "<<eps<<endl;
    }
    cout<<"Done reading Eps."<<endl;
    //fclose(stdin);
    cin.rdbuf(oldcin);
    fin.close();
    return 0;
}

int u2dFun::readFun()
{
    readEps((char *)"./function/eps");
    return 1;
}

int u2dFun::genBPts()
{
    // The diff equ: AX=b,
    // and this fun is 2 gen b pts.
    cout<<"Calcing b points"<<endl;
    phi=zeros2D(xNum,yNum);
    for(int k=0;k<xNum;k++){
        for(int p=0;p<yNum;p++){
            for(int q=0;q<nMax;q++){
                phi[k][p]+=aX[q]*cos(omegaX[q]*xMat[k][p]+phiX[q]);
                phi[k][p]+=aY[q]*cos(omegaY[q]*yMat[k][p]+phiY[q]);
            }
        }
    }
    cout<<"Done calc b points."<<endl;
    return 1;
}

int u2dOut::writeData(char *fileName,double **data)
{
    char filePath[256];
    strcpy(filePath,currentPath);
    strcat(filePath,fileName);
    ofstream fout(filePath);
    streambuf *oldcout;
    oldcout = cout.rdbuf(fout.rdbuf());

    for(int k=0;k<xNum;k++){
        for(int p=0;p<yNum;p++){
            cout<<data[k][p];
            if(p!=yNum-1){
                cout<<',';
            }
            else{
                cout<<endl;
            }
        }

    }

    cout.rdbuf(oldcout);
    return 1;
}